home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Games
/
Tetris
/
Source
/
Piece.m
< prev
next >
Wrap
Text File
|
1975-04-26
|
18KB
|
743 lines
#import <appkit/Application.h>
#import <appkit/NXImage.h>
#import <appkit/Window.h>
#import <dpsclient/wraps.h>
#import "Piece.h"
#import "TetMatrix.h"
// The number of pixels to move a block at once when it is dropped.
//static const float ANM_DELTA = 12.0;
static const float ANM_DELTA = 28.0;
extern long random();
extern void srandom(int seed);
extern int getpid();
@implementation Piece
#define PIECE_NULL (unsigned char *)0;
#define SHAPE_NULL (struct shape *)0;
const int NUM_ROTATIONS = 4;
const int NUM_SHAPES=7;
#define XMIN_BOUNDS(BOUNDS) ((BOUNDS & 0xc0) >> 6)
#define YMIN_BOUNDS(BOUNDS) ((BOUNDS & 0x30) >> 4)
#define XMAX_BOUNDS(BOUNDS) ((BOUNDS & 0xc) >> 2)
#define YMAX_BOUNDS(BOUNDS) (BOUNDS & 0x3)
- (void) setShapes
{
/* ** 1st shape ** */
shapes[0].table[0][0] = 0x0;
shapes[0].table[0][1] = 0xf; // ####
shapes[0].table[0][2] = 0x0;
shapes[0].table[0][3] = 0x0; // x,y x1,y1
shapes[0].bounds[0] = 0x2e; // (0, 2, 3, 2) 3*4 + 2 = 14 = 0xe
shapes[0].points[0] = 10; // This piece is worth 10 points
shapes[0].table[1][0] = 0x4; // #
shapes[0].table[1][1] = 0x4; // #
shapes[0].table[1][2] = 0x4; // #
shapes[0].table[1][3] = 0x4; // #
shapes[0].bounds[1] = 0x47; // (1, 0, 1, 3)
shapes[0].points[1] = 16;
shapes[0].table[2][0] = 0x0;
shapes[0].table[2][1] = 0xf; // ####
shapes[0].table[2][2] = 0x0;
shapes[0].table[2][3] = 0x0;
shapes[0].bounds[2] = 0x2e; // (0, 2, 3, 2)
shapes[0].points[2] = 10;
shapes[0].table[3][0] = 0x4; // #
shapes[0].table[3][1] = 0x4; // #
shapes[0].table[3][2] = 0x4; // #
shapes[0].table[3][3] = 0x4; // #
shapes[0].bounds[3] = 0x47; // (1, 0, 1, 3)
shapes[0].points[3] = 16;
/* ** 2nd shape ** */
shapes[1].table[0][0] = 0xc; // ##
shapes[1].table[0][1] = 0xc; // ##
shapes[1].table[0][2] = 0x0;
shapes[1].table[0][3] = 0x0;
shapes[1].bounds[0] = 0x27; // (0, 2, 1, 3)
shapes[1].points[0] = 12;
shapes[1].table[1][0] = 0xc; // ##
shapes[1].table[1][1] = 0xc; // ##
shapes[1].table[1][2] = 0x0;
shapes[1].table[1][3] = 0x0;
shapes[1].bounds[1] = 0x27; // (0, 2, 1, 3)
shapes[1].points[1] = 12;
shapes[1].table[2][0] = 0xc; // ##
shapes[1].table[2][1] = 0xc; // ##
shapes[1].table[2][2] = 0x0;
shapes[1].table[2][3] = 0x0;
shapes[1].bounds[2] = 0x27; // (0, 2, 1, 3)
shapes[1].points[2] = 12;
shapes[1].table[3][0] = 0xc; // ##
shapes[1].table[3][1] = 0xc; // ##
shapes[1].table[3][2] = 0x0;
shapes[1].table[3][3] = 0x0;
shapes[1].bounds[3] = 0x27; // (0, 2, 1, 3)
shapes[1].points[3] = 12;
/* ** 3rd shape ** */
shapes[2].table[0][0] = 0x4; // #
shapes[2].table[0][1] = 0xe; // ###
shapes[2].table[0][2] = 0x0;
shapes[2].table[0][3] = 0x0;
shapes[2].bounds[0] = 0x2b; // (0, 2, 2, 3)
shapes[2].points[0] = 10;
shapes[2].table[1][0] = 0x4; // #
shapes[2].table[1][1] = 0x6; // ##
shapes[2].table[1][2] = 0x4; // #
shapes[2].table[1][3] = 0x0;
shapes[2].bounds[1] = 0x5b; // (1, 1, 2, 3)
shapes[2].points[1] = 10;
shapes[2].table[2][0] = 0x0;
shapes[2].table[2][1] = 0xe; // ###
shapes[2].table[2][2] = 0x4; // #
shapes[2].table[2][3] = 0x0;
shapes[2].bounds[2] = 0x1a; // (0, 1, 2, 2)
shapes[2].points[2] = 12;
shapes[2].table[3][0] = 0x4; // #
shapes[2].table[3][1] = 0xc; // ##
shapes[2].table[3][2] = 0x4; // #
shapes[2].table[3][3] = 0x0;
shapes[2].bounds[3] = 0x17; // (0, 1, 1, 3)
shapes[2].points[3] = 10;
// 4th shape
shapes[3].table[0][0] = 0xc; // ##
shapes[3].table[0][1] = 0x6; // ##
shapes[3].table[0][2] = 0x0;
shapes[3].table[0][3] = 0x0;
shapes[3].bounds[0] = 0x2b; // (0, 2, 2, 3)
shapes[3].points[0] = 12;
shapes[3].table[1][0] = 0x4; // #
shapes[3].table[1][1] = 0xc; // ##
shapes[3].table[1][2] = 0x8; // #
shapes[3].table[1][3] = 0x0;
shapes[3].bounds[1] = 0x17; // (0, 1, 1, 3)
shapes[3].points[1] = 14;
shapes[3].table[2][0] = 0xc; // ##
shapes[3].table[2][1] = 0x6; // ##
shapes[3].table[2][2] = 0x0;
shapes[3].table[2][3] = 0x0;
shapes[3].bounds[2] = 0x2b; // (0, 2, 2, 3)
shapes[3].points[2] = 12;
shapes[3].table[3][0] = 0x4; // #
shapes[3].table[3][1] = 0xc; // ##
shapes[3].table[3][2] = 0x8; // #
shapes[3].table[3][3] = 0x0;
shapes[3].bounds[3] = 0x17; // (0, 1, 1, 3)
shapes[3].points[3] = 14;
// 5th shape
shapes[4].table[0][0] = 0x6; // ##
shapes[4].table[0][1] = 0xc; // ##
shapes[4].table[0][2] = 0x0;
shapes[4].table[0][3] = 0x0;
shapes[4].bounds[0] = 0x2b; // (0, 2, 2, 3)
shapes[4].points[0] = 12;
shapes[4].table[1][0] = 0x8; // #
shapes[4].table[1][1] = 0xc; // ##
shapes[4].table[1][2] = 0x4; // #
shapes[4].table[1][3] = 0x0;
shapes[4].bounds[1] = 0x17; // (0, 1, 1, 3)
shapes[4].points[1] = 14;
shapes[4].table[2][0] = 0x6; // ##
shapes[4].table[2][1] = 0xc; // ##
shapes[4].table[2][2] = 0x0;
shapes[4].table[2][3] = 0x0;
shapes[4].bounds[2] = 0x2b; // (0, 2, 2, 3)
shapes[4].points[2] = 12;
shapes[4].table[3][0] = 0x8; // #
shapes[4].table[3][1] = 0xc; // ##
shapes[4].table[3][2] = 0x4; // #
shapes[4].table[3][3] = 0x0;
shapes[4].bounds[3] = 0x17; // (0, 1, 1, 3)
shapes[4].points[3] = 14;
// 6th shape
shapes[5].table[0][0] = 0x2; // #
shapes[5].table[0][1] = 0xe; // ###
shapes[5].table[0][2] = 0x0;
shapes[5].table[0][3] = 0x0;
shapes[5].bounds[0] = 0x2b; // (0, 2, 2, 3)
shapes[5].points[0] = 6;
shapes[5].table[1][0] = 0x8; // #
shapes[5].table[1][1] = 0x8; // #
shapes[5].table[1][2] = 0xc; // ##
shapes[5].table[1][3] = 0x0;
shapes[5].bounds[1] = 0x17; // (0, 1, 1, 3)
shapes[5].points[1] = 7;
shapes[5].table[2][0] = 0xe; // ###
shapes[5].table[2][1] = 0x8; // #
shapes[5].table[2][2] = 0x0;
shapes[5].table[2][3] = 0x0;
shapes[5].bounds[2] = 0x2b; // (0, 2, 2, 3)
shapes[5].points[2] = 6;
shapes[5].table[3][0] = 0xc; // ##
shapes[5].table[3][1] = 0x4; // #
shapes[5].table[3][2] = 0x4; // #
shapes[5].table[3][3] = 0x0;
shapes[5].bounds[3] = 0x17; // (0, 1, 1, 3)
shapes[5].points[3] = 7;
// 7th shape
shapes[6].table[0][0] = 0xe; // ###
shapes[6].table[0][1] = 0x2; // #
shapes[6].table[0][2] = 0x0;
shapes[6].table[0][3] = 0x0;
shapes[6].bounds[0] = 0x2b; // (0, 2, 2, 3)
shapes[6].points[0] = 6;
shapes[6].table[1][0] = 0x4; // #
shapes[6].table[1][1] = 0x4; // #
shapes[6].table[1][2] = 0xc; // ##
shapes[6].table[1][3] = 0x0;
shapes[6].bounds[1] = 0x17; // (0, 1, 1, 3)
shapes[6].points[1] = 7;
shapes[6].table[2][0] = 0x8; // #
shapes[6].table[2][1] = 0xe; // ###
shapes[6].table[2][2] = 0x0;
shapes[6].table[2][3] = 0x0;
shapes[6].bounds[2] = 0x2b; // (0, 2, 2, 3)
shapes[6].points[2] = 6;
shapes[6].table[3][0] = 0xc; // ##
shapes[6].table[3][1] = 0x8; // #
shapes[6].table[3][2] = 0x8; // #
shapes[6].table[3][3] = 0x0;
shapes[6].bounds[3] = 0x17; // (0, 1, 1, 3)
shapes[6].points[3] = 7;
}
BOOL resize(NXSize *aSize)
{
BOOL resized = NO;
if (aSize->height > 32) {
aSize->height = 32;
resized = YES;
}
if (aSize->width > 32) {
aSize->width = 32;
resized = YES;
}
return resized;
}
- findBlockImages
{
int i;
char buffer[20];
id image;
char *blockName;
BOOL isGameColor = [NXApp isGameColor];
if (isGameColor) {
blockName = "ColorBlock";
} else {
blockName = "MonoBlock";
}
for (i=0; i<4; i++) {
sprintf(buffer, "%s%d", blockName, i+1);
image = [NXImage findImageNamed:buffer];
if (image == NULL) {
fprintf(stderr, "Couldn't find block named %s\n", buffer);
exit(0);
}
blockImage[i] = image;
}
// Assuming all the blocks are the same size.
[blockImage[0] getSize:&blockSize];
if (resize(&blockSize)) {
#ifdef DEBUG
printf("resizing Block Image\n");
#endif
[bitmap setScalable:YES];
[bitmap setSize:&blockSize];
}
return self;
}
- init
{
[super init];
[self setShapes];
curCol = curRow = 0;
currentPiece = PIECE_NULL;
currentShape = SHAPE_NULL;
currentRotation = shapenum = 0;
invalidRect.origin.x = invalidRect.origin.y =
invalidRect.size.width = invalidRect.size.height = 0.0;
// Initialized the animation Rect
{
NXSize tmp;
[self findBlockImages];
// Limit size of block image
[blockImage[0] getSize:&tmp];
resize(&tmp);
tmp.height *= 4;
tmp.width *= 4;
anmShape = [[NXImage alloc] initSize:&tmp];
[anmShape lockFocus];
PSsetalpha(0.0); // make this block transparent
PSrectfill(0., 0., tmp.width, tmp.height);
PSsetalpha(1.0);
}
anmBackground = nil;
viewVarsInited = NO;
dropPoints = 0;
srandom(getpid()); // Initialize random number generator
#ifdef DEBUG
printf("Piece object initialized\n");
#endif
return self;
}
/*
* Draw the Piece.
* The focus must be locked by on calling object's view before invoking this method.
*/
- draw:sender
{
unsigned char mask;
int xc, yc;
int ycmax;
NXPoint dPt;
ycmax = MAX_SHAPE_SIZE -
YMIN_BOUNDS(currentShape->bounds[currentRotation]);
for (yc = MAX_SHAPE_SIZE -
YMAX_BOUNDS(currentShape->bounds[currentRotation]) - 1;
yc < ycmax; yc++) {
mask = 1 << (MAX_SHAPE_SIZE - 1);
for (xc = 0; xc < MAX_SHAPE_SIZE; xc++) {
if (currentPiece[yc] & mask) {
// Call the sender's parent object
[sender point:&dPt for:curRow + MAX_SHAPE_SIZE - yc - 1 :curCol + xc];
[bitmap composite:NX_SOVER toPoint:&dPt];
}
mask >>= 1;
}
}
return self;
}
/*
* Determine what the next piece to drop will be.
*
*/
- newPiece
{
int bitmapNum;
#ifdef DEBUG
printf("Generating a new piece\n");
#endif
shapenum = random() % NUM_SHAPES;
// shapenum = 0;
currentShape = &shapes[shapenum];
currentRotation = random() % NUM_ROTATIONS;
currentPiece = currentShape->table[currentRotation];
bitmapNum = random() % NUM_BITMAPS;
bitmap = blockImage[bitmapNum];
if (bitmap == NULL) {
printf("Block %d not found\n", bitmapNum);
}
return self;
}
/*
* Needed so other objects can query to set the Window and View sizes.
*/
- getBlockSize:(NXSize *) size
{
*size = blockSize;
return self;
}
/*
* So other objects can query Piece.m about images
*/
- getBlockImage:(int) blockNum
{
return blockImage[blockNum];
}
- (struct pieceInfo *)pieceInfo
{
static struct pieceInfo info;
info.bitmap = bitmap;
info.shape = shapenum;
info.rotation = currentRotation;
return &info;
}
- setPiece:(struct pieceInfo *)info
{
bitmap = info->bitmap;
[bitmap getSize:&blockSize];
resize(&blockSize);
shapenum = info->shape;
currentShape = &shapes[shapenum];
currentRotation = info->rotation;
currentPiece = currentShape->table[currentRotation];
return self;
}
/*
* Get the next piece.
*
*/
- (BOOL)reset:sender piece:(struct pieceInfo *)info
{
BOOL legalMove;
unsigned char shapeBounds;
int xmin, xmax;
int ymin;
NXRect bounds;
#ifdef DEBUG
printf("reseting piece\n");
#endif
// info will contain the piece from NextMatrix if Show Next is selected
// Otherwise, we'll generate a new piece.
if (info) {
[self setPiece:info];
} else {
[self newPiece];
}
shapeBounds = currentShape->bounds[currentRotation];
xmin = XMIN_BOUNDS(shapeBounds);
xmax = XMAX_BOUNDS(shapeBounds);
ymin = YMIN_BOUNDS(shapeBounds);
curCol = (TETRIS_COLUMNS - (xmax - xmin + 1)) / 2;
curRow = TETRIS_ROWS - ymin - 1;
if (!viewVarsInited) {
[sender getBounds:&bounds];
[sender getIntercell:&intercell];
// Create a new animation background
anmBackground = [[NXImage alloc] initSize:&bounds.size];
viewVarsInited = YES;
}
if ((legalMove = [self legalMove:sender :curRow :curCol rotation:currentRotation])) {
[self getInvalidRect:&invalidRect for:sender];
[sender display:&invalidRect :1];
}
dropPoints = 0;
return legalMove;
}
- (BOOL)legalMove:sender :(int)row :(int)column rotation:(int)rotation
{
unsigned char mask;
int xc, yc;
int ycmax;
int ex, ey;
unsigned char *piece;
piece = currentShape->table[rotation];
ycmax = MAX_SHAPE_SIZE - YMIN_BOUNDS(currentShape->bounds[rotation]);
for (yc = MAX_SHAPE_SIZE -
YMAX_BOUNDS(currentShape->bounds[rotation]) - 1; yc < ycmax; yc++) {
mask = 1 << (MAX_SHAPE_SIZE - 1);
for (xc = 0; xc < MAX_SHAPE_SIZE; xc++) {
if (piece[yc] & mask) {
ey = row + MAX_SHAPE_SIZE - yc - 1;
ex = column + xc;
if (ex < 0 || ex >= TETRIS_COLUMNS || ey < 0
|| [sender bitmapAt:ey :ex])
return NO;
}
mask >>= 1;
}
}
return YES;
}
- left:sender
{
if ([self legalMove:sender :curRow :(curCol - 1) rotation:currentRotation]) {
curCol--;
[sender display:&invalidRect :1];
[self getInvalidRect:&invalidRect for:sender];
}
return self;
}
- right:sender
{
if ([self legalMove:sender :curRow :(curCol + 1) rotation:currentRotation]) {
curCol++;
[sender display:&invalidRect :1];
[self getInvalidRect:&invalidRect for:sender];
}
return self;
}
- getInvalidRect:(NXRect *)aRect for:sender
{
unsigned char bounds;
NXRect unionRect;
bounds = currentShape->bounds[currentRotation];
[sender getRect:&unionRect for:curRow + YMIN_BOUNDS(bounds) :curCol + XMIN_BOUNDS(bounds)];
[sender getRect: aRect for:curRow + YMAX_BOUNDS(bounds) :curCol + XMAX_BOUNDS(bounds)];
NXUnionRect(&unionRect, aRect);
return self;
}
- point:(NXPoint *)thePoint for:(int)row :(int)column
{
unsigned char bounds = currentShape->bounds[currentRotation];
int realx = column - curCol - XMIN_BOUNDS(bounds);
int realy = row - curRow - YMIN_BOUNDS(bounds);
thePoint->x = realx * (intercell.width + blockSize.width);
thePoint->y = realy * (intercell.height + blockSize.height);
return self;
}
/*
* Called when the player his the spacebar to drop the piece.
*/
- compositeShape:sender
{
if (anmShape) { // already exists
// [self->anmShape resize:self->invalidRect.size.width :self->invalidRect.size.height];
//self->anmShape = [NXImage newSize:&self->invalidRect.size];
} else { // create new image
//self->anmShape = [NXImage newSize:&self->invalidRect.size];
//.width
// :self->invalidRect.size.height type:NX_UNIQUEALPHABITMAP];
// [self->anmShape setFlip:NO];
}
[anmShape lockFocus];
PSsetalpha(0.0); // Fill the window with alpha
PSrectfill(0.0, 0.0, self->invalidRect.size.width, self->invalidRect.size.height);
PSsetalpha(1.0); // Draw image w/o alpha
[self draw:self];
[anmShape unlockFocus];
return self;
}
- compositeBackground: sender
{
NXRect unionRect;
[self getInvalidRect:&unionRect for:sender];
NXUnionRect(&self->invalidRect, &unionRect);
[anmBackground lockFocus];
[sender setPieceVisible:NO];
[sender drawSelf:&unionRect :1];
[sender setPieceVisible:YES];
[anmBackground unlockFocus];
return self;
}
/*
* Move the piece rapidly down the screen after the user hits the spacebar.
*
*/
- animateDrop:sender
{
NXRect updRect;
[self getInvalidRect:&updRect for:sender];
[sender lockFocus];
// Erase piece by compositing the background over it.
[anmBackground composite:NX_SOVER fromRect: &invalidRect
toPoint:&invalidRect.origin];
while (invalidRect.origin.y - ANM_DELTA > updRect.origin.y) {
invalidRect.origin.y -= ANM_DELTA;
// Show the piece dropping one increment
[anmShape composite:NX_SOVER toPoint:&invalidRect.origin];
[[sender window] flushWindow];
// Erase the piece by showing the background
[anmBackground composite:NX_SOVER fromRect:&invalidRect
toPoint:&invalidRect.origin];
}
[sender unlockFocus];
[anmShape lockFocus];
[anmShape getSize:&updRect.size];
PScompositerect(0.0, 0.0, updRect.size.width, updRect.size.height, NX_CLEAR);
[anmShape unlockFocus];
return self;
}
/*
* Return the amount of points that we get for this piece.
*/
- (int)points
{
return currentShape->points[currentRotation] + dropPoints;
}
/*
* Called when the user hits the space bar.
*
*/
- drop:sender
{
int ymin;
int yc;
ymin = YMIN_BOUNDS(currentShape->bounds[currentRotation]);
for (yc = curRow - 1; yc >= -1 - ymin; yc--) {
if (![self legalMove:sender :yc :curCol rotation:currentRotation])
break;
}
dropPoints += curRow - yc + 1; // You get extra points for dropping it
curRow = yc + 1;
[self compositeShape: sender];
[self compositeBackground: sender];
[self animateDrop: sender];
[sender display:&invalidRect : 1];
[self getInvalidRect:&invalidRect for:sender];
return self;
}
/*
* Move the piece down.
*/
- (BOOL)down:sender
{
BOOL canGoDown;
if ((canGoDown = [self legalMove:sender :curRow-1 :curCol rotation:currentRotation])){
curRow--;
[sender display:&invalidRect :1];
[self getInvalidRect:&invalidRect for:sender];
}
return canGoDown;
}
- turn:sender
{
int newRotation = (currentRotation + 3) % NUM_ROTATIONS;
if ([self legalMove:sender :curRow :curCol rotation:newRotation]) {
currentRotation = newRotation;
currentPiece = currentShape->table[newRotation];
[sender display:&invalidRect :1];
[self getInvalidRect:&invalidRect for:sender];
}
return self;
}
/*
* Called when the piece has reached the bottom and can no longer be moved.
*
* For each block that composes the image, put the block's image id in the
* proper iconMatrix[row][column] position. Thus to redraw the screen,
* we need only to composite the images from the iconMatrix.
*/
- stick:sender
{
unsigned char mask;
int col, theRow;
int rowMax;
rowMax = MAX_SHAPE_SIZE - YMIN_BOUNDS(currentShape->bounds[currentRotation]);
for (theRow = MAX_SHAPE_SIZE -
YMAX_BOUNDS(currentShape->bounds[currentRotation]) - 1;
theRow < rowMax; theRow++) {
mask = 1 << (MAX_SHAPE_SIZE - 1);
for (col = 0; col < MAX_SHAPE_SIZE; col++) {
// If there's a block in
if (currentPiece[theRow] & mask)
// Set the (row, column) of the matrix equal to Block's image.
[sender setBitmap:bitmap at:curRow + MAX_SHAPE_SIZE - theRow - 1
:curCol + col];
mask >>= 1;
}
}
return self;
}
- (int)getCurRow
{
return curRow;
}
- free
{
if (anmShape) [anmShape free];
if (anmBackground) [anmBackground free];
return [super free];
}
@end